﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using Chilkat;

namespace SeriousTalker
{
    public delegate void MessageReceived(object sender, EventArgs e);
    public delegate void ConnectionEstablished(object sender, EventArgs e);

    public class Server : IDisposable
    {
        private Socket _listeningSocket;
        private Socket _connectedSocket;
        private Thread _connectionThread;
        private Thread _serverThread;
        private Cert _certificate;
        private bool _stopRequested;
        
        public event ConnectionEstablished NewConnection;
        public event MessageReceived NewMessage;
        
        public string Message { get; set; }

        public Server(bool ssl)
        {
            _listeningSocket = new Socket();
            _listeningSocket.MaxReadIdleMs = 1000;
            _stopRequested = false;
            NewConnection += new ConnectionEstablished(Connection);

            try
            {
                UnLock();
            }
            catch (Exception e)
            {
                MessageBox.Show("Error: " + e.Message);
            }

            if(ssl)
            {
                try
                {
                    _certificate = LoadCertificate();
                    InitSslSocket(_certificate);
                }
                catch(Exception e)
                {
                    MessageBox.Show("Error: " + e.Message);
                }

            }

            try
            {
                BindAndListen();
            }
            catch (Exception e)
            {
                MessageBox.Show("Error: " + e.Message);
            }

            _connectionThread = new Thread(AcceptConnection);
            _connectionThread.Start();
            _serverThread = new Thread(ThreadProcedure);
        }

        private void InitSslSocket(Cert certificate)
        {
            if(!_listeningSocket.InitSslServer(certificate))
                throw new Exception("Error: " + _listeningSocket.LastErrorText);
        }

        private Cert LoadCertificate()
        {
            var certStore = new CertStore();
            var succes = certStore.LoadPfxFile("jernhulen.pfx", "copacabana");
            if(!succes)
                throw new Exception("Error loading certificate: " + certStore.LastErrorText);

            var cert = certStore.FindCertBySubject("KingOfTheHill");
            if(cert == null)
                throw new Exception("Error finding certificate: " + certStore.LastErrorText);

            return cert;
        }

        private void UnLock()
        {
            if (!_listeningSocket.UnlockComponent("Anything for 30-day trial"))
                throw new Exception("Failed to unlock component");
        }

        private void BindAndListen()
        {
            if(!_listeningSocket.BindAndListen(443, 5))
                throw new Exception("Failed to bind and listen to port.");
        }

        private void AcceptConnection()
        {
            _connectedSocket = _listeningSocket.AcceptNextConnection(200000);

            if(_connectedSocket == null)
                throw new Exception("No connections");

            _connectedSocket.MaxReadIdleMs = 5000;
            _connectedSocket.MaxSendIdleMs = 5000;

            if (NewConnection != null)
                NewConnection(this, EventArgs.Empty);
        }

        private void Connection(object sender, EventArgs e)
        {
            _serverThread.Start();
        }

        public void Stop()
        {
            _stopRequested = true;
        }

        private void ThreadProcedure()
        {
            while(true)
            {
                if (_stopRequested)
                    break;

                var receivedMsg = _connectedSocket.ReceiveString();
                if(receivedMsg != null)
                    if (NewMessage != null)
                    {
                        Message = receivedMsg;
                        NewMessage(this, EventArgs.Empty);
                    }
            }
        }

        public void Dispose()
        {
            if(_serverThread != null)
                _serverThread.Abort();

            if(_connectionThread != null)
                _connectionThread.Abort();
        }
    }
}
